#include <RenderSubSystem/CollectionNode.h>
#include <RenderSubSystem/Renderable.h>
#include <RenderSubSystem/Renderinglayer.h>
#include <RenderSubSystem/cvRenderlist.h>
#include <iterator>
#include <boost/intrusive/list.hpp>
#include <Logger/GlobalLogger.h>
namespace cvRenderInterface
{

	CollectionNode::CollectionNode()	{}
	CollectionNode::~CollectionNode()	{}

	void CollectionNode::destroy_list()
	{
		m_collectionlist.clear_and_dispose(renderinglist_element_disposer());
	}

	const bool CollectionNode::isSingleshot() const	{ return m_singleshot; }
	const bool CollectionNode::isCollection() const	{ return true; }

	bool CollectionNode::sort()
	{
		if(m_collectionlist.size() > 1)
		{
			renderinglist_it_t ft(renderinglist_t::s_iterator_to(*this));	// iterator to collection in mainlist
			m_collectionlist.sort(compare_drawnodes);

			++ft;	// move to next node in mainlist.
			// TODO: algorithm is partial: Check also the node before.
			if(ft != m_layer->renderList.end())
			{
				if(ft->isCollection())
				{
					CollectionNode & batch = static_cast<CollectionNode&>(*ft);
					batch.sort();	// sort the collection. (NOTE: at worst, this causes recursion until end of the mainlist)

					// test for overlap
					if(higherBound() < batch.lowerBound())
					{	// have to split off the conflickting part. At worst, all but one nodes are purged back to main list.
						renderinglist_it_t cut = ++m_collectionlist.begin();	// start at second renderable in collection
						while(cut != m_collectionlist.end())
						{
							if(cut->drawPriority() > static_cast<CollectionNode&>(*ft).lowerBound())
							{
								break;
							}
						}
						LOGDEBUG("::RENDER()#3: purging conflicting nodes from CollectionNode for sort");
						m_collectionlist.erase_and_dispose(cut, m_collectionlist.end(),
															renderinglist_element_mover(m_layer->renderList, ft));
						m_is_avgprorityvalid = false;
						return true;
					}
				}
			}
		}
		return false;
	}

	float CollectionNode::drawPriority() const
	{

		if(!m_is_avgprorityvalid)
		{
			float priority = 0.0f;
			for(renderinglist_cit_t it = m_collectionlist.begin(); it != m_collectionlist.end(); ++it)
			{
				priority += it->drawPriority();
			}
			m_draworder = priority / m_collectionlist.size();
			m_is_avgprorityvalid = true;
			return m_draworder;
		}
		else
		{
			return m_draworder;
		}
	}

	float CollectionNode::lowerBound()
	{
		if(!m_collectionlist.empty())
		{
			return m_collectionlist.back().drawPriority();
		}
		else
		{
			return m_draworder;
		}
	}

	float CollectionNode::higherBound()
	{
		if(!m_collectionlist.empty())
		{
			return m_collectionlist.front().drawPriority();
		}
		else
		{
			return m_draworder;
		}
	}

	renderinglist_t & CollectionNode::getNodes()	{ return m_collectionlist; }

	size_t CollectionNode::range_size( renderinglist_it_t it, const renderinglist_it_t end ) const
	{
		size_t size = 0;
		while(it != end)
		{
			if(it->isCollection())
			{
				size += static_cast<CollectionNode&>(*it).m_collectionlist.size();
			}
			else
			{
				++size;
			}
			++it;
		}
		return size;
	}

	void CollectionNode::append_range( renderinglist_it_t & begin, renderinglist_it_t & end )
	{	// Append range into *this.m_collecetionlist from mainlist
		// [begin, end) is a range in mainlist.

		renderinglist_it_t it, nit, invbegin;
		it = nit = invbegin = begin;
		while(it != end)
		{
			if(it->isCollection())
			{
				if(nit != it)
				{	// append nodes. [nit, it) is removed from mainlist.
					m_collectionlist.splice(m_collectionlist.end(), m_layer->renderList, nit, it);
				}
				// append collection. note: *it node is not removed!
				m_collectionlist.splice(m_collectionlist.end(), static_cast<CollectionNode&>(*it).m_collectionlist);

				if(invbegin == nit)	{ invbegin = it; }
				++it;
				nit = it;
			}
			else
			{
				++it;
				if(it == end)
				{
					m_collectionlist.splice(m_collectionlist.end(), m_layer->renderList, nit, it);
				}
			}
		}
		// set invalidated range
		begin = invbegin;
		end = nit;
		m_is_avgprorityvalid = false;
	}
};
